﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.DirectX;
using Microsoft.DirectX.DirectSound;

using System.IO;
using System.Threading;

namespace P2PChat.Media.Recorder
{
    public class SoundRecorder
    {
        #region 私有成员
        private Notify cacheNotify = null;
        private int iNotifyNum = 16;
        private int iBufferOffset = 0;
        private int iSampleSize = 0;
        private int iNotifySize = 0;
        private int iBufferSize = 0;
        private Capture capture = null;
        private CaptureBuffer captureBuffer = null;
        private AutoResetEvent notifyEvent = null;
        private Thread notifyThread = null;
        private WaveFormat wavFormat;   // PCM格式
        #endregion

        #region 公共成员
        /// <summary>
        /// 负责类中的日志
        /// </summary>
        public P2PChat.Log.ILog iLog;
        public event EventHandler<SoundRecorderEventArgs> OnCapturedData;
        /// <summary>
        /// 使用的捕捉设备的序号
        /// </summary>
        public int captureDeviceIndex = 0;
        #endregion

        #region 私有方法
        private WaveFormat SetWaveFormat()
        {
            WaveFormat format = new WaveFormat();
            format.FormatTag = WaveFormatTag.Pcm;//设置音频类型
            format.SamplesPerSecond = 22050;//采样率（单位：赫兹）典型值：11025、22050、44100Hz
            format.BitsPerSample = 16;//采样位数
            format.Channels = 1;//声道
            format.BlockAlign = (short)(format.Channels * (format.BitsPerSample / 8));//单位采样点的字节数
            format.AverageBytesPerSecond = format.BlockAlign * format.SamplesPerSecond;
            return format;
            //按照以上采样规格，可知采样1秒钟的字节数为22050*2=55100B 约为 53K
        }

        private bool CreateCaptureDevice()
        {
            // 枚举可用捕捉设备
            CaptureDevicesCollection capDevices = new CaptureDevicesCollection();
            Guid devGuid;
            if (capDevices.Count > 0)
            {
                // 检查当前设备是否存在
                if (capDevices.Count >= captureDeviceIndex)
                {
                    devGuid = capDevices[captureDeviceIndex].DriverGuid;
                }
                else
                {
                    if (iLog != null)
                    {
                        iLog.Add("无法初始化选中的录音设备，默认使用第一个录音设备。");
                    }
                    devGuid = capDevices[0].DriverGuid;
                }
            }
            else
            {
                if (iLog != null)
                {
                    iLog.Add("未检测到任何录音设备！");
                }
                throw new Microsoft.DirectX.DirectSound.NoDriverException();
            }
            capture = new Capture(devGuid);
            return true;
        }

        private void CreateCaptureBuffer()
        {
            CaptureBufferDescription bufferDescription = new CaptureBufferDescription();
            bufferDescription.Format = wavFormat;
            iNotifySize = 1024;
            iBufferSize = iNotifyNum * iNotifySize;
            bufferDescription.BufferBytes = iBufferSize;
            captureBuffer = new CaptureBuffer(bufferDescription, capture);
        }

        //设置通知
        private void CreateNotification()
        {
            BufferPositionNotify[] bpn = new BufferPositionNotify[iNotifyNum];//设置缓冲区通知个数
            //设置通知事件
            notifyEvent = new AutoResetEvent(false);
            notifyThread = new Thread(RecoData);
            notifyThread.Start();
            for (int i = 0; i < iNotifyNum; i++)
            {
                bpn[i].Offset = iNotifySize + i * iNotifySize - 1;//设置具体每个的位置
                bpn[i].EventNotifyHandle = notifyEvent.SafeWaitHandle.DangerousGetHandle();
            }
            cacheNotify = new Notify(captureBuffer);
            cacheNotify.SetNotificationPositions(bpn);

        }

        //线程中的事件
        private void RecoData()
        {
            while (true)
            {
                // 等待缓冲区的通知消息
                notifyEvent.WaitOne(Timeout.Infinite, true);
                // 录制数据
                RecordCapturedData();
            }
        }

        //真正转移数据的事件，其实就是把数据转移到WAV文件中。
        private void RecordCapturedData()
        {
            short[] capturedata = null;
            int readpos = 0, capturepos = 0, locksize = 0;
            captureBuffer.GetCurrentPosition(out capturepos, out readpos);
            locksize = readpos - iBufferOffset;//这个大小就是我们可以安全读取的大小
            if (locksize == 0)
            {
                return;
            }
            if (locksize < 0)
            {// 因为我们是循环的使用缓冲区，所以有一种情况下为负：当文以载读指针回到第一个通知点，而IBuffeOffset还在最后一个通知处
                locksize += iBufferSize;
            }

            capturedata = (short[])captureBuffer.Read(iBufferOffset, typeof(short), LockFlag.FromWriteCursor, locksize);

            if (OnCapturedData != null)
            {
                SoundRecorderEventArgs e = new SoundRecorderEventArgs(capturedata);
                OnCapturedData(this, e);
            }

            iSampleSize += capturedata.Length;
            iBufferOffset += capturedata.Length;
            iBufferOffset %= iBufferSize;//取模是因为缓冲区是循环的。
        }

        private void stoprec()
        {
            captureBuffer.Stop();//调用缓冲区的停止方法。停止采集声音
            if (notifyEvent != null)
                notifyEvent.Set();//关闭通知
            notifyThread.Abort();//结束线程
            RecordCapturedData();//将缓冲区最后一部分数据写入到文件中
        }
        #endregion
    }
}
